/*
  Ejemplo de programa servidor SDP. Atiende peticiones aritmeticas, el protocolo de transporte sera el 
  de la plataforma SDP original (sdp::Transport) y el contenido del mensaje sera el resultado de un 
  comm::Codec con los (x, y, op) -> El resultado sera estos tres componente mas result.

  Los clientes pueden ser: http_kclient.p
*/

#include <iostream>

#include <nemesis.h>
#include <nemesis.comm.h>

#include <nemesis.xml.Node.h>
#include <nemesis.xml.Attribute.h>

#include <nemesis.sdp.Application.h>
#include <nemesis.sdp.Interface.h>
#include <nemesis.sdp.Message.h>

#include <test.Request.h>
#include <test.Response.h>
#include <test.SDPCommunicator.h>

using namespace std;
using namespace test;

class MyInterface : public sdp::Interface {
public:
   MyInterface (sdp::Communicator& sdpCommunicator) : 
      sdp::Interface (sdpCommunicator, sdp::Communicator::DefaultInterface, "server SDP-SDP")
   {;}

   void receive (comm::Server&, const sdp::Message&) throw (RuntimeException);

private:
   test::Request a_request;
   test::Response a_response;
   
   sdp_complete_interface (MyInterface);
};       

class MyCommunicator : public test::SDPCommunicator {
public: 
   MyCommunicator (sdp::Application& application) :
      test::SDPCommunicator (application),
      a_interface (*this)
   {}
   
private:
   MyInterface a_interface;
};

class SDPArithmeticServer : public sdp::Application {
public:
   SDPArithmeticServer ();
   
private:
   MyCommunicator a_communicator;

   void prepareCommandLine (CommandLine&) const throw ();
   void run () throw (RuntimeException);    
   xml::Node* asXML (xml::Node* app) const throw ();
};

using namespace std;

int main (int argc, const char** argv)
{
   SDPArithmeticServer app;
   
   try {
      app.initCommandLine (argv, argc);
      app.start ();
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }
   
   return 0;
}

SDPArithmeticServer::SDPArithmeticServer () :
   sdp::Application ("sdp_server", "Servidor de operaciones aritmeticas con protocolo SDP-SDP", "1.0"),
   a_communicator (*this)
{
}

void SDPArithmeticServer::prepareCommandLine (CommandLine& cl) const 
   throw ()
{
   sdp::Application::prepareCommandLine (cl);
   
   cl.add ("d", CommandLine::Argument::Mandatory, "Retardo aplicado a la contestacio");
   cl.add ("n", CommandLine::Argument::Optional, "Numero de mensajes a servir", true);
   cl.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
   cl.add ("limit", CommandLine::Argument::Optional, "% de ocupacion que permitimos");
}   

void SDPArithmeticServer::run () 
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("sdp_server", "run", FILE_LOCATION));

   sdp::Application::run ();

   CommandLine& cl (CommandLine::instanciate ());

   a_communicator.setDelay (cl.getIntegerValue ("d"));
 
   if (cl.exists ("n") == true) {
      int n = cl.getIntegerValue ("n");
      a_communicator.setMaxMessage (n);
      cout << "MaxMessage: " << n << endl << endl;
   }

   if (cl.exists ("trace"))
      Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));

   if (cl.exists ("limit")) 
      CongestionController::instance ().setLimit (cl.getIntegerValue ("limit"));
   
   a_communicator.accept ();
}

xml::Node* SDPArithmeticServer::asXML (xml::Node* app) const 
   throw ()
{
   xml::Node* node = sdp::Application::asXML (app);
   
   node->createAttribute ("MaxMessage", a_communicator.getMaxMessage ());
   node->createAttribute ("Message", a_communicator.getMessage ());
   
   return node;
}

void MyInterface::receive (Server& server, const sdp::Message& message)
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("MyInterface", "receive", FILE_LOCATION));

   SDPCommunicator& communicator = static_cast <SDPCommunicator&> (getCommunicator ());

   if (communicator.canContinue (this) == false)
      return;

   communicator.delay ();  
     
   a_request.decode (message.getBody ());

   a_response.x = a_request.x;
   a_response.y = a_request.y;
   a_response.initTime = a_request.initTime;

   switch (a_response.op = a_request.op) {
      case '+':
         a_response.result = a_request.x + a_request.y;
         break;
      case '-':
         a_response.result = a_request.x - a_request.y;
         break;
      case '*':
         a_response.result = a_request.x * a_request.y;
         break;
      case '/':
         a_response.result = (a_request.y != 0) ? (a_request.x / a_request.y): 0;
         break;
   }
   
   LOGINFORMATION (
      string msg = nemesis::functions::asString ("%d %c %d = %d", a_request.x, a_request.op, a_request.y, a_response.result);
      msg += nemesis::functions::asText (" | InitTime: ",  a_response.initTime);
      Logger::information (msg, FILE_LOCATION);
   )

   try {
      reply (server, a_response);
      communicator.resetError ();
   }
   catch (RuntimeException& ex) {
      communicator.notifyError ();
      ex.trace ();
   }
}

